import hashlib
import hmac
import json
import requests
import time
import urllib.parse
import sys
import datetime

from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum

class OrderSide(Enum):
  BUY = "BUY"
  SELL = "SELL"

class OrderType(Enum):
  LIMIT = "LIMIT"
  MARKET = "MARKET"
  LIMIT_MAKER = "LIMIT_MAKER"
  IMMEDIATE_OR_CANCEL = "IMMEDIATE_OR_CANCEL"
  FILL_OR_KILL = "FILL_OR_KILL"

class MexcAuth(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)

  def _gen_signature(self, query_string=None):
    t = str(int(round(time.time() * 1000)))
    s = self._key.access_key + t + query_string.query
    sign = hmac.new(self._key._secret_key_bytes,
                    s.encode('utf-8'), hashlib.sha256).hexdigest()
    return {'ApiKey': self._key.access_key, 'Request-Time': str(t), 'Signature': sign}

  def __call__(self, req: requests.PreparedRequest):
    request = urllib.parse.urlparse(req.url)
    signature = self._gen_signature(request)
    req.headers.update({'Accept': 'application/json',
                       'Content-Type': 'application/json'})
    req.headers.update(signature)
    return req

class MexcAuthV3(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)

  def _gen_signature(self, data: bytes):
    signature = hmac.new(self._key.secret_key_bytes, data, digestmod=hashlib.sha256)
    return signature.hexdigest()

  def __call__(self, req: requests.PreparedRequest):
    query = urllib.parse.urlparse(req.url).query
    if req.body:
      signature = self._gen_signature(query.encode() + req.body.encode())
    else:
      signature = self._gen_signature(query.encode())
    req.headers.update({'X-MEXC-APIKEY': self._key.access_key})

    req.prepare_headers(req.headers)
    req.prepare_url(req.url, {"signature": signature})

    print(req.headers)
    print(req.url)
    print(req.body)

    return req

class MexcPrivateClient():
  def __init__(self, key, mea, ip=None):
    self._key = key
    self._mea = mea
    if self._mea == "Spot.Mexc.v2":
      self._url = 'https://www.mexc.com'
      self._auth = MexcAuth(key)
    elif self._mea == "Spot.Mexc.v3":
      self._url = "https://api.mexc.com"
      self._auth = MexcAuthV3(key)
    else:
      assert (0)

    self._query = SyncQueryer(api_host=self._url, auth=self._auth, ip=ip)

  def __check_v3(self):
    if self._mea != "Spot.Mexc.v3":
      assert (0)

  def get_open_order(self, symbol):
    method = "GET"
    path = "/api/v3/openOrders"
    params = {
      "symbol": symbol,
      "timestamp": int(time.time_ns() / 1000000)
    }

    response = self._query.query(method=method, path=path, params=params)
    return response

  def submit_order(
      self,
      symbol: str,
      side: OrderSide,
      type: OrderType,
      quantity: str = None,
      quote_order_qty: str = None,
      price: str = None,
      new_client_order_id: str = None,
    ):
    assert symbol, "symbol must be not None and not empty!"
    assert side, "side must be not None!"
    assert type, "type must be not None!"

    method = "POST"
    path = "/api/v3/order"
    data = {
      "symbol": symbol,
      "side": side.value,
      "type": type.value,
      "quantity": quantity,
      "quoteOrderQty": quote_order_qty,
      "price": price,
      "newClientOrderId": new_client_order_id,
      "timestamp": int(time.time_ns() / 1000000),
    }
    
    response = self._query.query(method=method, path=path, data=data)
    return response

  def submit_batch_order(self):
    method = "POST"
    path = "/api/v3/batchOrders"
    data = {
      "batchOrders": """
        [{\"symbol\":\"MXUSDT\",\"side\":\"BUY\",\"type\":\"LIMIT_MAKER\",\"quantity\":\"2.00\",\"price\":\"2.6053\",\"newClientOrderId\":\"58747_1687847797307261138\"},
        {\"symbol\":\"MXUSDT\",\"side\":\"BUY\",\"type\":\"LIMIT_MAKER\",\"quantity\":\"2.00\",\"price\":\"2.6053\",\"newClientOrderId\":\"58747_1687847797307261139\"},
        {\"symbol\":\"MXUSDT\",\"side\":\"BUY\",\"type\":\"LIMIT_MAKER\",\"quantity\":\"2.00\",\"price\":\"2.6053\",\"newClientOrderId\":\"58747_1687847797307261140\"}]
      """,
      "timestamp": int(time.time_ns() / 1000000),
    }
    
    response = self._query.query(method=method, path=path, params=data)
    return response

  def cancel_order(self, id):
    method = "DELETE"
    path = "/open/api/v2/order/cancel"
    params = {"order_ids": id}
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def query_transfer(self, from_account: str, to_account: str):  # SPOT", "FUTURES", "ISOLATED_MARGIN
    self.__check_v3()
    method = "GET"
    path = "/api/v3/capital/sub-account/universalTransfer"
    params = {"fromAccountType": from_account, "toAccountType": to_account,
              "timestamp": int(time.time_ns() / 1000000)}
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def account_balance(self):
    path = "/api/v3/account"
    method = "GET"
    params = {
      "timestamp": int(time.time_ns() / 1000000)
    }
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def sub_account_list(self):
    path = "/open/api/v2/account/info"
    method = "GET"
    response = self._query.query(
        method=method, path=path)
    return response

  def query_deposit(self, start_time: datetime.datetime, end_time: datetime.datetime):
    path = "/open/api/v2/asset/deposit/list"
    method = "GET"
    params = {"end_time": int(end_time.timestamp() * 1000),
                              "start_time": int(start_time.timestamp() * 1000)}
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def query_withdraw(self, start_time: datetime.datetime, end_time: datetime.datetime):
    path = "/open/api/v2/asset/withdraw/list"
    method = "GET"
    params = {"end_time": int(end_time.timestamp() * 1000),
                              "start_time": int(start_time.timestamp() * 1000)}
    response = self._query.query(
        method=method, path=path, params=params)
    return response


if __name__ == "__main__":
  key_path = sys.argv[1]
  #config = {"key_path": key_path, "mea": "Spot.Mexc.v2", "native_symbols": ["JOY_USDT"]}
  mexc = MexcPrivateClient(key_path, "Spot.Mexc.v3")
  #response = mexc.submit_order("BTCUSDT", OrderSide.BUY, OrderType.LIMIT, "50", None, "1", "tianqiangtest2023031401")
  response = mexc.get_open_order("LTCUSDT")
  res_json = response.json()
  print(json.dumps(res_json, indent=2))

  #response = mexc.sub_account_list()
  #response = mexc.account_balance()
  #response = mexc.withdraw()
  #response = mexc.transfer_record()
  #print(json.dumps(response, indent=2))
  #print(response, json.dumps(response, indent=2))
